<script>
import YmFormField from './YmFormField'
import {NAME, WIDGET, LABEL} from "@/store/elementui"
import {COL, GROUP_FIELD, FIELDS, getDefaultValue, ARRAY_FIELD} from "@/store/form"
import Util from '@/util'

/**
 * @typedef { object } Listener
 * @property { function } input
 * @property { function } change
 * @property { function } click
 *
 */

/**
 * @typedef { object } Field
 * @property { string } [WIDGET]
 * @property { string } labelPosition
 * @property { string } [NAME]
 * @property { array } [FIELDS]
 */

/**
 * @param {{ [NAME]: string }} field
 * @param {{}} obj
 * @return {{ name: string, value: any }}
 */
function getNameAddValue(field, obj) {
  let name = field[NAME] ? field[NAME] : undefined
  let value = (field[NAME] && obj[field[NAME]] !== undefined) ? obj[field[NAME]] : getDefaultValue(field)

  return { name, value }
}

function getFn (key, obj, listeners, name, fn) {
  return (v) => {
    if (!name) return
    const o = {
      ...obj,
      [name]: fn(v)
    }
    listeners[key] && listeners[key](o)
  }
}

/**
 * @param {string} name
 * @param {{}} obj
 * @param { Listener } listeners
 * @param { function } fn
 * @return { Listener }
 */
function getListeners (name, listeners, obj, fn = v =>v ) {
  return {
    ...listeners,
    input: getFn('input', obj, listeners, name, fn),
    change: getFn('change', obj, listeners, name, fn)
  }
}

/**
 * 绘制域
 * @param { Field } field 域
 * @param {CreateElement} h vNode生成函数
 * @param {number} index 序号
 * @param { {} } obj value
 * @param {{ labelPosition: ?string }} props 参数
 * @param { Listener } listeners 事件处理器
 */
function renderField(field, index, h, obj, listeners, props) {

  if (field[WIDGET] === GROUP_FIELD) return renderGroup(field, index, h, obj, listeners)

  if (field[WIDGET] === ARRAY_FIELD) return renderArray(field, index, h, obj, listeners)

  let { name, value } = getNameAddValue(field, obj)

  // 处理disable
  let disabled = Util.isDisabled(field, obj)
  if (disabled && name && obj[name] !== undefined) delete obj[name]

  //
  let labelPosition = field.labelPosition || props.labelPosition

  let span = field[COL] || 24

  const setter = (name) => {
    return (v) => {
      if (name) {
        obj[name] = v
        listeners.input && listeners.input(obj)
      }
    }
  }

  const deleter = (name) => {
    return () => {
      if (name && obj[name] !== undefined) {
        delete obj[name]
        listeners.input && listeners.input(obj)
      }
    }
  }

  const onClick = listeners.click

  let vNode = h(YmFormField, {
    props: {
      ...field,
      value,
      disabled,
      labelPosition
    },
    on: {
      ...listeners,
      input: setter(name),
      change: setter(name),
      delete: deleter(name)
    }
  })

  return h('el-col', {
    props: {
      span
    },
    nativeOn: {
      click: () => {
        onClick && onClick(field, index)
      }
    }
  }, [vNode])
}

/**
 * 生成 group
 * @param { Field } field 域
 * @param {CreateElement} h vNode生成函数
 * @param {number} index 序号
 * @param {{}} obj value
 * @param { Listener } listeners 事件处理器
 */
function renderGroup(field, index, h, obj, listeners) {
  let { name, value } = getNameAddValue(field, obj)

  const on = getListeners(name, listeners, obj)

  return (
    <el-col span={24}>
      <el-row >
        <el-col span={24}>
          <el-divider content-position="left">{field[LABEL]}</el-divider>
        </el-col>
        <el-col span={23} offset={1} class="ym-group-content">
          {renderFields(h, value, field[FIELDS], on, field)}
        </el-col>
      </el-row>
    </el-col>
  )
}

/**
 * 生成 group
 * @param { Field } field 域
 * @param {CreateElement} h vNode生成函数
 * @param {number} index 序号
 * @param {{}} obj value
 * @param { Listener } listeners 事件处理器
 */
function renderArray(field, index, h, obj, listeners) {
  let { name, value } = getNameAddValue(field, obj)

  let fnAdd = getFn('change', obj, listeners, name, () => {
    let v = [...value]
    v.push({})
    return v
  })

  let fnRemove = index => getFn('change', obj, listeners, name, () => {
    let v = [...value]
    v.splice(index, 1)
    return v
  })

  let on = index => getListeners(name, listeners, obj, (v) => {
    let va = [...value]
    va.splice(index, 1, v)
    return va
  })

  return (
    <el-col span={24}>
      <el-row>
        <el-col span={24}>
          <el-divider content-position="left">{field[LABEL]}</el-divider>
        </el-col>
        {
          value.map((v, index) => {
            return [
              <el-col span={20} offset={1} className="ym-group-content">
                { renderFields(h, v, field[FIELDS], on(index), field) }
              </el-col>,
              <el-col span={3}>
                <el-button onClick={fnRemove(index)} type="primary" icon="el-icon-delete" class="btn-width" />
              </el-col>
            ]
          })
        }
        <el-col span={24}>
          <el-button onClick={fnAdd} type="primary" icon="el-icon-add" class="btn-width" />
        </el-col>
      </el-row>
    </el-col>
  )
}

/**
 * 生成一组表单域
 * @param {CreateElement} h vNode生成函数
 * @param {{}} obj value
 * @param { array } fields 参数
 * @param { Listener } listeners 事件处理器
 * @param { { labelPosition: ? string }} ext
 */
function renderFields(h, obj, fields, listeners, ext = {}) {
  if (!fields) {
    console.log('render fields error', fields)
    return []
  }

  const vNodes = fields.map((field, index) => {
    return renderField(field, index, h, obj, listeners, ext)
  })

  return h('el-row', {
    props: {
      gutter: 20
    }
  }, vNodes)
}

/**
 * props { fields: array<filed>, value }
 *   响应 delete 事件
 */
export default {
  name: 'YmFormFields',
  functional: true,
  render (h, context) {
    const { props, listeners } = context

    // 表单值对象
    const obj = {
      ...props.value
    }

    return renderFields(h, obj, props[FIELDS], listeners, props)
  }
}
</script>

<style>
.ym-group-content {
  margin-top: -24px;
  padding: 24px;
  border: 1px solid #DCDFE6;
  border-top: 0;
}

.btn-width {
  width: 100%;
}
</style>
